#include <sstream>
#include <xscript/xvariabletable.h>
#include <xscript/xprint.h>
#include <xscript/xscriptexecutor.h>

XVariableTable::XVariableTable() {
    addModule(L"");
}

void XVariableTable::printTable() {
    print(std::wcout, "==========================");
    print(std::wcout, "output XVariableTable Data");
    print(std::wcout, "XVariableTable size:", moduleSet.size());

    for (auto& set : moduleSet) {
        print(std::wcout, L"module:", set.first, "size:", set.second->size());
        auto itr = set.second->begin();
        for (; itr != set.second->end(); itr++) {
            print(std::wcout, L"block:", itr->first);
            for (auto& var : itr->second->data)
                print(std::wcout, L"  var", var.first, var.second->getItemInfo());
        }
    }
    print(std::wcout, "==========================");
}

bool XVariableTable::setCurrentModule(const std::wstring& moduleName) {
    if (currentModuleName != moduleName) {
        currentModuleName = moduleName;
        auto itr = addModule(currentModuleName);
        currentModule = itr->second;
        return true;
    }

    if (!currentModule) {
        auto itr = addModule(currentModuleName);
        currentModule = itr->second;
    }
    return true;
}

std::shared_ptr<XScopedBlockMap> XVariableTable::getCurrentModule() const {
    return currentModule;
}

bool XVariableTable::setCurrentBlock(uint32_t blockLine) {
    if (!currentModule) {
        XScriptExecutor::getInstance()->setError(XError::XError_GenericError, L"not set current module");
        return false;
    }

    if (blockLine == currentBlockLine)
        return true;

    currentBlockLine = blockLine;

    auto itr = currentModule->find(blockLine);
    if (itr != currentModule->end()) {
        currentBlock = itr->second;
        return true;
    }

    currentBlock = std::make_shared<XScopedBlock>();
    currentBlock->line = blockLine;
    currentModule->insert(std::make_pair(blockLine, currentBlock));
    itr = currentModule->find(blockLine);
    return true;
}

std::shared_ptr<XScopedBlock> XVariableTable::getCurrentBlock() const {
    return currentBlock;
}

XVariableTableSet::iterator XVariableTable::addModule(const std::wstring& modulName) {
    auto itr = moduleSet.find(modulName);
    if (itr == moduleSet.end())
        moduleSet.insert(std::make_pair(modulName, std::make_shared<XScopedBlockMap>()));
    else
        itr = moduleSet.find(modulName);
    return itr;
}

bool XVariableTable::addBlock(const std::wstring& modulName, uint32_t blockLine, const std::wstring& blockName, std::shared_ptr<XScopedBlock> parent) {
    bool set = setCurrentModule(modulName);
    if (!set) {
        XScriptExecutor::getInstance()->setError(XError::XError_GenericError, std::wstring(L"not set module:") + modulName);
        return false;
    }
    set = setCurrentBlock(blockLine);
    if (!set) {
        XScriptExecutor::getInstance()->setError(XError::XError_GenericError, std::wstring(L"not set block:") + std::to_wstring(blockLine));
        return false;
    }
    currentBlock->name = blockName;
    currentBlock->parent = parent;
    return true;
}

bool XVariableTable::addVariable(const std::wstring& variable, std::shared_ptr<XItem> item) {
    if (!currentBlock) {
        XScriptExecutor::getInstance()->setError(XError::XError_GenericError, std::wstring(L"not set current block"));
        return false;
    }

    currentBlock->data.insert_or_assign(variable, item);
    return true;
}

bool XVariableTable::setVariable(const std::wstring& variable, std::shared_ptr<XItem> item) {
    if (!currentBlock) {
        XScriptExecutor::getInstance()->setError(XError::XError_GenericError, std::wstring(L"not set current block"));
        return false;
    }

    if (currentBlock->data.find(variable) != currentBlock->data.end())
        currentBlock->data.insert_or_assign(variable, item);
    else {
        auto [var, line] = getVariable(variable);
        if (var) {
            setCurrentBlock(line);
            setVariable(variable, item);
        } else
            return false;
    }
    return true;
}

bool XVariableTable::removeVariable(const std::wstring& varName) {
    if (!currentBlock) {
        XScriptExecutor::getInstance()->setError(XError::XError_GenericError, std::wstring(L"not set current block"));
        return false;
    }

    auto itr = currentBlock->data.find(varName);
    if (itr == currentBlock->data.end()) {
        XScriptExecutor::getInstance()->setError(XError::XError_VariableExistedError, varName);
        return false;
    }
    currentBlock->data.erase(itr);
    return true;
}

std::tuple<XItemPtr, uint32_t> XVariableTable::getVariable(const std::wstring& varName) {
    auto block = currentBlock;

    while (true) {
        if (!block)
            break;

        auto map = block->data;
        for (auto& data : map) {
            if (data.first == varName) {
                return std::make_tuple(data.second, block->line);
            }
        }

        block = block->parent;

    }
    return std::make_tuple(nullptr, ~0);
}





